package com.zmj.sy.mom.srv.aps.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zmj.sy.mom.srv.aps.bean.bo.CacheFactory;
import com.zmj.sy.mom.srv.aps.bean.entity.base.BaseEntity;
import com.zmj.sy.mom.srv.aps.bean.entity.order.ApsBatch;
import com.zmj.sy.mom.srv.aps.bean.entity.order.ApsOrder;
import com.zmj.sy.mom.srv.aps.bean.entity.order.Order;
import com.zmj.sy.mom.srv.aps.bean.entity.order.OrderPhase;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.*;
import com.zmj.sy.mom.srv.aps.bean.vo.order.OrderSchedulingReqVo;
import com.zmj.sy.mom.srv.aps.config.ProjectConfig;
import com.zmj.sy.mom.srv.aps.config.StrategyConfig;
import com.zmj.sy.mom.srv.aps.mapper.*;
import com.zmj.sy.mom.srv.aps.utils.ApsConstant;
import com.zmj.sy.mom.srv.aps.utils.SyExceptionUtils;
import com.zmj.sy.mom.srv.aps.utils.schedulerole.ScheduleRole;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
public class OrderSchedulingService {

    private final StringRedisTemplate stringRedisTemplate;

    private final OrderMapper orderMapper;
    private final ApsWorkOrderMapper apsWorkOrderMapper;
    private final ApsWorkDetailMapper apsWorkDetailMapper;
    private final ApsDetailWeekdayMapper apsDetailWeekdayMapper;
    private final ApsOrderMapper apsOrderMapper;
    private final StrategyConfig strategyConfig;
    private final ApsBatchMapper apsBatchMapper;
    private final ProjectConfig projectConfig;
    private final CustomerMapper customerMapper;
    private final OrderPhaseMapper orderPhaseMapper;
    private final GroupMapper groupMapper;
//    private final StationGroupMapper stationGroupMapper;
    private final StationMapper stationMapper;
    private final StationWeekdaySchemeMapper stationWeekdaySchemeMapper;
    private final StationWeekdayRuleMapper stationWeekdayRuleMapper;
    private final ShiftTypeMapper shiftTypeMapper;
    private final ShiftMapper shiftMapper;
    private final BreakPeriodMapper breakPeriodMapper;

    @Transactional
    public void scheduling(OrderSchedulingReqVo reqVo) {

        CacheFactory orderCache = new CacheFactory();
        orderCache.setSchedulingRule(reqVo.getSchedulingRule());

        // 生成一个批次，并且和订单关联
        ApsBatch apsBatch = apsBatchMapper.lambdaQuery().eq(ApsBatch::getStatus, 1).one();
        if (apsBatch == null) {
            apsBatch = genBatch(reqVo);
            apsBatchMapper.insert(apsBatch);
        } else {
            modifyBatch(apsBatch);
            apsBatchMapper.updateById(apsBatch);
        }
        orderCache.getAllBatch().add(apsBatch);


        // 排产前的检查
        List<Order> orderList = orderMapper.lambdaQuery()
                .in(Order::getOrderStatus, 3, 5)
                .in(Order::getOrderType, 1, 3, 4)
                .list();
        orderCache.getAllOrder().addAll(orderList);
        for (Order order : orderList) {
            order.setBatchId(apsBatch.getId());
        }

        // 校验
        preOrders(orderCache);



        // 开始排产
        schedulingOrders(orderCache);

        //

    }

    public void modifyBatch(ApsBatch apsBatch) {
        LocalDateTime now = LocalDateTime.now();
        apsBatch.setBatchNo(now.format(DatePattern.NORM_DATETIME_FORMATTER));
        apsBatch.setSchedulingDateTime(now);
    }

    public ApsBatch genBatch(OrderSchedulingReqVo reqVo) {
        LocalDateTime now = LocalDateTime.now();
        ApsBatch apsBatch = new ApsBatch();
        apsBatch.setBatchNo(now.format(DatePattern.NORM_DATETIME_FORMATTER));
        apsBatch.setSchedulingRule(reqVo.getSchedulingRule());
        apsBatch.setSchedulingDateTime(now);
        apsBatch.setStatus(1);
        return apsBatch;
    }

    public void schedulingOrders(CacheFactory schedulingCache) {

        Map<Integer, List<Order>> batchOrderGroup = schedulingCache.getAllOrder().stream().collect(Collectors.groupingBy(Order::getBatchId));

        // 构建工厂缓存信息
        CacheFactory apsFactoryCache = genApsFactoryCache(schedulingCache);


        for (ApsBatch batch : schedulingCache.getAllBatch()) {

            List<Order> orders = batchOrderGroup.get(batch.getId());

            // 查询子订单并给子订单排序
            ScheduleRole scheduleRole = strategyConfig.getScheduleRole(batch.getSchedulingRule());
            scheduleRole.sort(schedulingCache, batch, orders);

        }



    }

    public CacheFactory genApsFactoryCache(CacheFactory cacheFactory) {

        // 设置工厂信息
        cacheFactory.setFactoryCode(projectConfig.getFactoryCode());

        // 设置客户信息
        List<Integer> customerList = cacheFactory.getAllOrder().stream().map(Order::getCustomerId).distinct().collect(Collectors.toList());
        if(CollUtil.isNotEmpty(customerList)){
            List<Customer> customers = customerMapper.selectBatchIds(customerList);
            cacheFactory.setCustomers(customers);
        }

        // 工作组信息
        List<String> groupNumberString = orderPhaseMapper.selectGroupList(Wrappers.<OrderPhase>query()
                .in("order_id", cacheFactory.getAllOrder().stream().map(BaseEntity::getId).collect(Collectors.toList()))
        );
        List<Group> groups = groupMapper.lambdaQuery().in(Group::getMyCode, groupNumberString).list();
        cacheFactory.setGroups(groups);

        // 工作组工位关联
//        List<StationGroup> stationGroups = stationGroupMapper.selectList(Wrappers.<StationGroup>lambdaQuery().in(StationGroup::getGroupId, groups.stream().map(BaseEntity::getId).collect(Collectors.toList())));
//        cacheFactory.setStationGroups(stationGroups);

        // 工位信息
//        List<Integer> stationIds = stationGroups.stream().map(StationGroup::getStationId).distinct().collect(Collectors.toList());
//        List<Station> stations = stationMapper.selectBatchIds(stationIds);
        List<Station> stations = stationMapper.lambdaQuery().in(Station::getGroupId, groups.stream().map(BaseEntity::getId).collect(Collectors.toList())).list();
        List<Integer> stationIds = stations.stream().map(Station::getId).collect(Collectors.toList());

        for (Station station : stations) {
            if(station.getEnterTime() == null || station.getEnterTime() < 0){
                station.setEnterTime(0);
            }

            if(station.getOutTime() == null || station.getOutTime() < 0){
                station.setOutTime(0);
            }

            if(station.getChangTime() == null || station.getChangTime() < 0){
                station.setChangTime(0);
            }
        }

        cacheFactory.setStations(stations);

        // 快捷从工作组找到主工位列表
//        Map<Integer, List<StationGroup>> stationGroupMap = stationGroups.stream().filter(e -> e.getDefaultGroup().equals(1)).collect(Collectors.groupingBy(StationGroup::getGroupId));
        Map<Integer, Station> stationMap = stations.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));
        Map<Integer, List<Station>> stationGroupIdMap = stations.stream().collect(Collectors.groupingBy(Station::getGroupId));
        Map<String, List<Station>> masterStationMap = new HashMap<>();
        for (Group group : groups) {

//            List<StationGroup> stationGroups1 = stationGroupMap.get(group.getId());
//            if(CollUtil.isEmpty(stationGroups1)){
//                continue;
//            }

//            List<Station> value = stationGroupMap.get(group.getId())
//                    .stream()
//                    .map(e -> stationMap.get(e.getStationId()))
//                    .collect(Collectors.toList());
            List<Station> value = stationGroupIdMap.get(group.getId());

            masterStationMap.put(group.getMyCode(), value);
        }

        cacheFactory.setMasterStationMap(masterStationMap);

        // 工位日历
        List<StationWeekdayScheme> stationWeekdaySchemes = stationWeekdaySchemeMapper.selectList(Wrappers.<StationWeekdayScheme>lambdaQuery().in(StationWeekdayScheme::getStationId, stationIds));
        List<StationWeekdayRule> stationWeekdayRules = stationWeekdayRuleMapper.selectList(Wrappers.<StationWeekdayRule>lambdaQuery().in(StationWeekdayRule::getStationId, stationIds));
        List<Integer> shiftTypeIds = stationWeekdaySchemes.stream().map(StationWeekdayScheme::getShiftTypeId).distinct().collect(Collectors.toList());
        List<ShiftType> shiftTypes = shiftTypeMapper.selectBatchIds(shiftTypeIds);
        List<Shift> shifts = shiftMapper.selectList(Wrappers.<Shift>lambdaQuery().in(Shift::getShiftTypeId, shiftTypeIds));
        List<BreakPeriod> breakPeriods = breakPeriodMapper.selectList(Wrappers.<BreakPeriod>lambdaQuery().in(BreakPeriod::getShiftId, shifts.stream().map(BaseEntity::getId).distinct().collect(Collectors.toList())));

        cacheFactory.setStationWeekdaySchemes(stationWeekdaySchemes);
        cacheFactory.setStationWeekdayRules(stationWeekdayRules);
        cacheFactory.setShiftTypes(shiftTypes);
        cacheFactory.setShifts(shifts);
        cacheFactory.setBreakPeriods(breakPeriods);

        // 拼装设备时间链
        /*
        LocalDate startDate = reqVo.getStartDateTime().toLocalDate();
        int day = 10;
        LocalDate endDate = startDate.plusDays(day);
        cacheFactory.setMachines(stations.stream().map(ApsMachine::new).collect(Collectors.toList()));
        cacheFactory.addDay(startDate, day);


        // 生产工单占位填充
        LambdaQueryWrapper<ApsDetailWeekday> wrapper = Wrappers.lambdaQuery();
        wrapper.ge(ApsDetailWeekday::getShiftDay, startDate);
        wrapper.le(ApsDetailWeekday::getShiftDay, endDate);
        wrapper.ge(ApsDetailWeekday::getState, 2);
        wrapper.ne(ApsDetailWeekday::getState, 4);
        wrapper.eq(ApsDetailWeekday::getOrderType, 1);
        wrapper.in(ApsDetailWeekday::getStationId, stations.stream().map(Station::getId).distinct().collect(Collectors.toList()));
        wrapper.orderByAsc(ApsDetailWeekday::getPlanStartDateTime);
        List<ApsDetailWeekday> apsDetailWeekdays = apsDetailWeekdayMapper.selectList(wrapper);

        if(CollUtil.isNotEmpty(apsDetailWeekdays)){
            List<Integer> collect1 = apsDetailWeekdays.stream().map(ApsDetailWeekday::getApsWorkDetailId).distinct().collect(Collectors.toList());
            Map<Integer, ApsWorkDetail> detailMap = apsWorkDetailMapper.selectBatchIds(collect1)
                    .stream()
                    .collect(Collectors.toMap(ApsWorkDetail::getId, Function.identity()));

            cacheFactory.addTasks(apsDetailWeekdays, detailMap);
        }


        Map<String, List<ApsMachine>> groupCodeMachineMap = new HashMap<>();
        Map<Integer, ApsMachine> idApsMachineMap = cacheFactory.getMachines().stream().collect(Collectors.toMap(ApsMachine::getId, Function.identity()));

        for (Map.Entry<String, List<Station>> entry : masterStationMap.entrySet()) {
            groupCodeMachineMap.put(entry.getKey(), entry.getValue().stream().map(e -> idApsMachineMap.get(e.getId())).collect(Collectors.toList()));
        }

        // 激光切割工作组合并
        List<String> joinGroup = Arrays.asList("1-2_JGQG_00", "1-3_JGQG_00");
        List<ApsMachine> temp = new ArrayList<>();
        for (String groupCode : joinGroup) {
            // 取出原本工作组的数据加入缓存
            List<ApsMachine> apsMachines = groupCodeMachineMap.get(groupCode);
            if(apsMachines == null){
                continue;
            }
            temp.addAll(apsMachines);
            // 缓存的指针给加工机器，以便多个工作组共享
            groupCodeMachineMap.put(groupCode, temp);
        }


        cacheFactory.setGroupCodeMachineMap(groupCodeMachineMap);

        cacheFactory.setIgnorePhaseCode(projectConfig.getIgnorePhaseCodeList());

        cacheFactory.setPzCode(WorkshopProp.PZ_PHASE_CODE);
        cacheFactory.setPzSecond(WorkshopProp.PZ_SECOND);

        return apsFactoryCache;

         */
        return null;
    }

    public void preOrders(CacheFactory reqVo) {
        // 1.检查redis是否有锁定状态，如果有则提示，并且不排产
        ValueOperations<String, String> valOperation = stringRedisTemplate.opsForValue();
        String lockFlag = valOperation.get(ApsConstant.APS_SCHEDULING_LOCK);
        if (StringUtils.hasText(lockFlag)) {
            throw SyExceptionUtils.e("{} 等订单排产中!", lockFlag);
        }

        // 2.如果是未锁定，看有没有待排产的订单
        if(CollUtil.isEmpty(reqVo.getAllOrder())){
            throw SyExceptionUtils.e("没有待排产的订单");
        }

        // 3.把待排产的订单号放入redis，设置锁定状态
        String redisVal = reqVo.getAllOrder().stream().map(e -> e.getConstructNo() + "_" + e.getMaterName()).collect(Collectors.joining(", "));
        valOperation.set(ApsConstant.APS_SCHEDULING_LOCK, redisVal, 1, TimeUnit.DAYS);

        // 4.开始执行清理工作
        for (Order order : reqVo.getAllOrder()) {

            // 重置子订单的排产时间
            apsOrderMapper.lambdaUpdate()
                    .set(ApsOrder::getPlanStartDateTime, null)
                    .set(ApsOrder::getPlanEndDateTime, null)
                    .in(ApsOrder::getOrderId, order.getId())
                    .update();

            // 删除工单
            apsWorkOrderMapper.deleteOrderId(order.getId());

            // 删除工单详情
            apsWorkDetailMapper.deleteOrderId(order.getId());

            // 删除详情日历
            apsDetailWeekdayMapper.deleteOrderId(order.getId());
        }

    }
}
